package com.xyzq.cgate.core.plugin.routing;

import com.google.common.base.Joiner;
import com.xyzq.cgate.common.constant.Constants;
import com.xyzq.cgate.common.enums.PluginDetailEnum;
import com.xyzq.cgate.common.rule.custom.entity.BalancePiece;
import com.xyzq.cgate.common.rule.custom.entity.ComplexRule;
import com.xyzq.cgate.common.rule.custom.entity.LoadBalance;
import com.xyzq.cgate.common.rule.custom.entity.MatchCondition;
import com.xyzq.cgate.core.cache.BalanceManager;
import com.xyzq.cgate.core.cache.CacheManager;
import com.xyzq.cgate.core.component.hystrix.HttpObservableCommand;
import com.xyzq.cgate.core.component.hystrix.HystrixObservableBuilder;
import com.xyzq.cgate.core.component.loadbalance.LoadBalanceFactory;
import com.xyzq.cgate.core.component.loadbalance.LoadBalanceSelector;
import com.xyzq.cgate.core.plugin.AbstractPlugin;
import com.xyzq.cgate.core.plugin.IPluginChain;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URLEncoder;
import java.util.Map;

/**
 * @Description:
 * @author chenchen
 * @date 2019/6/19 14:11
 */
public class HttpPlugin extends AbstractPlugin {

    private static final Logger LOGGER = LoggerFactory.getLogger(HttpPlugin.class);

    private final CacheManager cacheManager;

    private final BalanceManager balanceManager;

    public HttpPlugin(final CacheManager cacheManager, final BalanceManager balanceManager) {
        super(cacheManager);
        this.cacheManager = cacheManager;
        this.balanceManager = balanceManager;
    }

    @Override
    protected Mono<Void> doExecute(ServerWebExchange exchange, IPluginChain chain, ComplexRule rule) {

        Map<String, LoadBalance> balanceMap = balanceManager.getBalanceMap();

        String requestUrl;

        LoadBalance loadBalance = balanceMap.get(rule.getPluginName() + "_" + URLEncoder.encode(rule.getRuleName()));

        LoadBalanceSelector loadBalanceSelector = LoadBalanceFactory.of(loadBalance.getStrategy());
        BalancePiece selectBalancePiece = loadBalanceSelector.select(loadBalance, rule.getMatchCondition().getIp());

        MatchCondition requestCondition = (MatchCondition) exchange.getAttributes()
                .get(Constants.REQUESTDTO);
        String server = (String)exchange.getAttributes().get(Constants.GRAY_RELEASE_LOADBALANCE);
        if(StringUtils.isNotBlank(server)) {
            requestUrl = buildUrl(server, requestCondition.getMethod());
        } else {
            requestUrl = buildUrl(selectBalancePiece.getServer(), requestCondition.getMethod());
        }

        HttpObservableCommand command = new HttpObservableCommand(
                HystrixObservableBuilder.build(rule.getHystrixParam()),
                exchange,
                chain,
                rule.getMatchCondition(),
                requestUrl,
                Long.parseLong(rule.getLoadBalance().getRetryTime()));

        return Mono.create(sink -> {
            command.toObservable().subscribe(sink::success,
                    sink::error, sink::success);
            //s.onCancel(sub::unsubscribe);
        }).doOnError(e -> {
            e.printStackTrace();
            chain.execute(exchange);
        }).then();

    }

    private String buildUrl(String server, String method) {
        StringUtils.trim(server);
        if(!server.contains("http")) {
            server = "http://" + server;
        }
        if(method.startsWith("/")) {
            method = method.substring(1);
        }
        return Joiner.on("/").join(server, method);
    }

    @Override
    public String getPluginPhase() {
        return PluginDetailEnum.HTTP.getPhase();
    }

    @Override
    public int getCode() {
        return PluginDetailEnum.HTTP.getCode();
    }

    @Override
    public String getName() {
        return PluginDetailEnum.HTTP.getName();
    }
}
